﻿using BootstrapBlazor.Components;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Ports;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using Windows.Devices.Sms;
using static zy_simu.Data.DevPreDef;

namespace zy_simu.Data
{
    public class DeviceSnap
    {
        public readonly int TASK_PERIOD = 50;
        public readonly byte ZQM_IO_NA = 0x80;

        //private int dev_state = 0;

        // m0/cat1 simulator process handle
        private Process? m0_process = null;
        private Process? cat1_process = null;

        // 基本设备属性
        public uint dev_type = 0;               // 设备类型
        // ADC
        public byte adc_chn_num = 4;                // ADC channel数量， 目前固定4个：ADC_CHN_NTC1, ADC_CHN_NTC2, ADC_CHN_VBAT, ADC_CHN_VREF
        public ushort[] adc = new ushort[4];        // ADC value
        private ushort[] last_adc = new ushort[4];   // ADC value
        // PWM
        public byte pwm_chn_num = 2;            // fixed 2 channels: PWM1, PWM2
        public byte[] pwm = new byte[2];        // PWM value
        // key
        public byte entity_key_num = 0;         // 实体键数量
        public byte virtual_key_num = 0;        // 虚拟键数量
        public byte[] key_tb = null!;           // key 描述 - 对应显示从左到右, 从上到下顺序的实体键对应的virtual key(DispKeyIdx)
        public byte key_val { get; set; } = 0;  // key value, send to device
        public uint[] key_state { get; set; } = null!;                  // key state, recv from device
        // display
        //public byte disp_memory_size { get; set; } = 0;
        public byte[] disp_memory { get; set; } = new byte[8];
        public byte digital_num { get; set; } = 4;        // led数码管数量
        //public byte[] led_digital_mode { get; set; } = new byte[10];  // led数码管mode
        // cat1 属性
        public int _4g_state { get; set; } = -1;

        public ZqmParamsTable[] pm_tb = new ZqmParamsTable[0];  // parameters display name, minimum value, maxmum value, map, and value
        public ZyStates st_tb = new ZyStates(); // device state


        public DiagRespCfg resp_cfg = new();
        public List<(byte, short)> pm_set = new(); // (pm索引, 参数值)
        public List<(byte, uint)> st_set = new(); // (st索引, 参数值)




        private SimuCfg cfg = null!;

        public DeviceSnap()
        {
            for (int i = 0; i < disp_item_codec_tb.Length; i++)
            {
                disp_item_codec_tb[i] = new DispItemCodecTb();
            }

        }

        private EmuCommPipe virtual_port1 = null!;
        private EmuCommPipe virtual_port2 = null!;
        private IEmuComm comm = null!;
        private Task comm_recv_task = null!;
        private bool comm_recv_task_cancel = false;

        private Process m0_console = null!;
        private Process cat1_console = null!;
        private StringBuilder m0_log_buf = new();
        private StringBuilder cat1_log_buf = new();
        public string m0_log = "";
        public string cat1_log = "";

        public bool Start(SimuCfg cfg)
        {
            this.cfg = cfg;

            dev_type = 0;               // 设备类型
            // ADC
            adc_chn_num = 4;                // ADC channel数量， 目前固定4个：ADC_CHN_NTC1, ADC_CHN_NTC2, ADC_CHN_VBAT, ADC_CHN_VREF
            adc = new ushort[4];        // ADC value
            last_adc = new ushort[4];   // ADC value
            // PWM
            pwm_chn_num = 2;            // fixed 2 channels: PWM1, PWM2
            pwm = new byte[2];        // PWM value
            // key
            entity_key_num = 0;         // 实体键数量
            virtual_key_num = 0;        // 虚拟键数量
            key_tb = null!;           // key 描述 - 对应显示从左到右, 从上到下顺序的实体键对应的virtual key(DispKeyIdx)
            key_val = 0;  // key value, send to device
            key_state = null!;                  // key state, recv from device
            // display
            disp_memory = new byte[8];
            digital_num = 4;        // led数码管数量
            // cat1 属性
            _4g_state = -1;

            pm_tb = new ZqmParamsTable[0];  // parameters display name, minimum value, maxmum value, map, and value
            st_tb = new ZyStates(); // device state

            resp_cfg = new();
            pm_set = new(); // (pm索引, 参数值)
            st_set = new(); // (st索引, 参数值)


            // add two virtual ports for simu device
            virtual_port1 = new EmuCommPipe();
            virtual_port2 = new EmuCommPipe();

            //// 启动M0 log console
            //if (m0_console == null) m0_console = KgConsoleManager.StartChildConsole("M0");
            //// 启动cat1 log console
            //if (cat1_console == null) cat1_console = KgConsoleManager.StartChildConsole("Cat1");

            // 根据设备或者功能 判断是监控m0还是cat1
            if (cfg.m0_mode == "仿真") M0PowerOn();
            if (cfg.m0_mode == "关闭" && cfg.cat1_mode == "仿真") Cat1PowerOn(); // 无M0控制cat1，启动单cat1仿真设备，一般是纯定位器等
            //switch (cfg.m0_mode)
            //{
            //    case "仿真":      // 启动M0仿真设备
            //        M0PowerOn();
            //        break;
            //    case "外设":      // 挂真实设备，启动控制台接收log
            //        //KgConsoleManager.Show();
            //        Process child1 = KgConsoleManager.StartChildConsole("M0");
            //        child1.StandardInput.WriteLine("M0 test test!");
            //        Process child2 = KgConsoleManager.StartChildConsole("Cat1");
            //        child2.StandardInput.WriteLine("Cat1 test test!");
            //        break;
            //    case "关闭":      // 无M0控制cat1，启动单cat1仿真设备，一般是纯定位器等
            //        if (cfg.cat1_mode == "仿真")  // 无M0控制cat1，启动单cat1仿真设备，一般是纯定位器等
            //            Cat1PowerOn(); 
            //        break;
            //    default:
            //        Debug.Assert(false);
            //        break;
            //}
            //Thread.Sleep(100);

            // config simulator communication interface
            if (cfg.mon_mode == "串口")
            {
                if (cfg.mon_port == "虚拟接口-1")
                    comm = virtual_port1;
                else if (cfg.mon_port == "虚拟接口-2")
                    comm = virtual_port2;
                else if (cfg.mon_port.StartsWith("COM"))
                    comm = new EmuCommUart(cfg.mon_port);
            }
            else if (cfg.mon_mode == "蓝牙")
            {
                comm = new EmuCommBle(cfg.mon_port.Replace(":", ""));
            }
            Debug.Assert(comm != null);

            // start device communication
            Debug.Assert(comm.Open().Result);
            comm_recv_task = Task.Run(CommRecv);
            // wait for device config, 200ms retry(CommWaitResponse will wait for 200ms), for 120s
            int i = 0;
            for (; i < (120*5*(TASK_PERIOD * 4)) && dev_type == 0; i += (TASK_PERIOD * 4))
                QueryCfgDev();
            Debug.Assert(dev_type != 0);
            Thread.Sleep(TASK_PERIOD); // simu 50ms task period
            if(cfg.params_monitor == true)
            {
                for (; i < (120 * 5 * (TASK_PERIOD * 4)) && pm_tb.Length == 0; i += (TASK_PERIOD * 4))
                    QueryCfgPm();
                Debug.Assert(pm_tb != null);
                Thread.Sleep(TASK_PERIOD); // simu 50ms task period
            }
            if(cfg.states_monitor == true)
            {
                for (; i < (120 * 5 * (TASK_PERIOD * 4)) && st_buf == null; i += (TASK_PERIOD * 4))
                    QueryCfgSt();
                Debug.Assert(st_buf != null);
                st_tb = KgStreamConvert.BytesToStuct<ZyStates>(st_buf);
            }

            return true;
        }
        public void Stop()
        {
            // TODOOO 先关闭仿真设备，再关闭通信接口，不然管道的readfile会阻塞卡死
            dev_type = 0;
            M0PowerOff();
            // close comm task
            comm_recv_task_cancel = true;
            for (int i = 0; i < 50 && comm_recv_task_cancel; i++, Thread.Sleep(20)) ; // wait recv task end
            comm.Close();
            System.Console.WriteLine("DevComm Stop");


            Cat1PowerOff();
        }

        private byte[] pm_buf = null!;       // parameter value buffer
        private byte[] st_buf = null!;       // ZyStates buffer
        public void Sync()
        {
            QuerySnapshot();
            // 还原pm table
            for (int i = 0; i < pm_tb.Length; i++)
            {
                pm_tb[i].val = (short)(pm_buf[i * 2] | (pm_buf[i * 2 + 1] << 8));
            }
            // 还原st table
            if(st_buf != null)
                st_tb = KgStreamConvert.BytesToStuct<ZyStates>(st_buf);
        }
        public void Cat1Command(string cmd)
        {
            if(dev_type != 0 && cat1_process != null)
            {
                string[] op = cmd.Split("=");
                if (op[0] == "4g")
                {
                    int val = int.Parse(op[1]);
                    if (val != _4g_state)
                    {
                        _4g_state = val;
                        cat1_process!.StandardInput.WriteLine(cmd);
                        cat1_process!.StandardInput.Flush();
                    }
                }
            }
        }

        public void WriteGPIO(byte io_num, bool value)
        {
            if (!IsGPIOValid(io_num)) return;
            gpio_val = value ? (gpio_val | ((ulong)1 << io_num)) : (gpio_val & (~((ulong)1 << io_num)));
        }
        public bool ReadGPIO(byte io_num)
        {
            if(!IsGPIOValid(io_num)) return false;
            return ((gpio_val & ((ulong)1 << io_num)) != 0);
        }
        public bool IsGPIOValid(byte io_num)
        {
            return (ZQM_IO_NA != io_num);
        }
        private void M0PowerOn()
        {
            if (m0_process != null) return;
            ProcessStartInfo startInfo = new ProcessStartInfo("m0.exe");
            // 不从操作系统的shell启动，这样继承父进程的标准流和匿名管道等资源
            startInfo.UseShellExecute = false;
            startInfo.RedirectStandardInput = true;   // 重定向输入 - 接收dbg cmd
            startInfo.RedirectStandardOutput = true;  // 重定向输出, log输出使用stderr
            //// 不开启自己的控制台来输出log
            //startInfo.CreateNoWindow = true;
            // 配置m0的4个串口
            for (int i = 0; i < 4; i++)
            {
                string port = "NULL";
                if (cfg.m0_port[i] == "虚拟接口-1")
                    port = virtual_port1.GetClientPort();
                else if (cfg.m0_port[i] == "虚拟接口-2")
                    port = virtual_port2.GetClientPort();
                else if (cfg.m0_port[i].StartsWith("COM"))
                    port = cfg.m0_port[i];
                startInfo.ArgumentList.Add(port);
            }
            // 启动M0
            m0_process = Process.Start(startInfo);
        }
        private void M0PowerOff()
        {
            //if (m0_console != null)
            //{
            //    m0_console.Kill(); // close console
            //    m0_console = null;
            //}

            if (m0_process != null)
            {
                try 
                { 
                    m0_process.Kill();
                }
                catch(Exception e) 
                { 
                    Debug.WriteLine(e.Message); 
                }
                //m0_process.Kill();
                m0_process.WaitForExit(1000);
                m0_process.Close();
                m0_process = null;
            }
        }
        // Start/stop cat1 simulator
        public void Cat1PowerOn()
        {
            if (cat1_process != null) return;
            if (cfg.cat1_mode != "仿真") return;
            ProcessStartInfo startInfo = new ProcessStartInfo("cat1.exe");
            // 不从操作系统的shell启动，这样继承父进程的标准流和匿名管道等资源
            startInfo.UseShellExecute = false;
            startInfo.RedirectStandardInput = true;   // 重定向输入 - 接收dbg cmd
            startInfo.RedirectStandardOutput = true;  // 重定向输出, log输出使用stderr
            // 不开启自己的控制台来输出log
            //startInfo.CreateNoWindow = true;

            // 配置cat1的2个串口
            for (int i = 0; i < 2; i++)
            {
                string port = "NULL";
                if (cfg.cat1_port[i] == "虚拟接口-1")
                    port = virtual_port1.GetServerPort();
                else if (cfg.cat1_port[i] == "虚拟接口-2")
                    port = virtual_port2.GetServerPort();
                else if (cfg.cat1_port[i].StartsWith("COM"))
                    port = cfg.cat1_port[i];
                startInfo.ArgumentList.Add(port);
            }
            // 启动Cat1
            cat1_process = Process.Start(startInfo);
            // 如果通道是为子进程创建的，这里关闭多余的handle。
            if (cfg.cat1_port[0] == "虚拟接口-1")
            {
                virtual_port1.Close();
            }
            if (cfg.cat1_port[0] == "虚拟接口-2")
            {
                //virtual_port2.Close();
            }
        }
        public void Cat1PowerOff()
        {
            //if (cat1_console != null)
            //{
            //    cat1_console.Kill(); // close console
            //    cat1_console = null;
            //}

            if (cat1_process != null)
            {
                cat1_process.Kill();
                cat1_process.Close();
                cat1_process = null;
            }
            _4g_state = -1;
            // kg modified 2025-02-08, 不用关闭模拟器端的port, m0-cat1也可以运作，同时避免再次启动cat1时，虚拟串口被关闭
            //if (cfg.cat1_port[0] == "虚拟接口-1")
            //{
            //    virtual_port1 = null;
            //}
            //if (cfg.cat1_port[0] == "虚拟接口-2")
            //{
            //    virtual_port2 = null;
            //}
        }












        public void ChangePmTable(int idx, short value)
        {
            if (idx >= 0 && idx < pm_tb.Length)
            {
                pm_set.Add(((byte)idx, value));
            }
        }
        public void ChangeStTable(int idx, int value)
        {
            if(idx < 0) return;
            if (st_buf == null) return;
            System.Reflection.FieldInfo[] fields = st_tb.GetType().GetFields();
            Type t = fields[idx].FieldType;
            if (t == typeof(byte))
            {
                fields[idx].SetValue(st_tb, (byte)value);
            }
            else if (t == typeof(short))
            {
                fields[idx].SetValue(st_tb, (short)value);
            }
            else if (t == typeof(ushort))
            {
                fields[idx].SetValue(st_tb, (ushort)value);
            }
            else
            {
                fields[idx].SetValue(st_tb, value);
            }
            byte[] st_buf_mod = KgStreamConvert.Struct2Bytes(st_tb);
            for (int i = 0; i < st_buf_mod.Length; i += 4)
            {
                uint x = BitConverter.ToUInt32(st_buf_mod, i);
                uint y = BitConverter.ToUInt32(st_buf, i);
                if (x != y)
                {
                    st_set.Add(((byte)i, x));
                }
            }
        }
        private bool is_gpio_changed = true;
        private bool is_adc_changed = true;
        public void ChangeGPIO(bool IsChanged)
        {
            is_gpio_changed = IsChanged;
        }


        private enum CommDiagCmd  // device diagnostic communication config query command 
        {
            NONE = 0,       // none
            LOG = 0x16,     // communication diagnostic command: print log
            LOG_CAT1,       // communication diagnostic command: print cat1 log
            CFG_DEV,        // communication diagnostic command: query device base info: type, display panel type, io table
            CFG_PM,         // communication diagnostic command: query parameters table
            CFG_ST,         // communication diagnostic command: query state table
            SNAP,           // communication diagnostic command:  swap io, param and st value
        }
        private CommDiagCmd comm_send_cmd = CommDiagCmd.NONE;
        private byte[] comm_recv_buf = new byte[1024 * 2];
        private byte[] comm_frame_data = new byte[1024 * 2];
        private int comm_recv_cnt = 0;
        private void CommRecv()
        {
            while (!comm_recv_task_cancel) // recv task loop
            {
                //System.Threading.Thread.Sleep(10);
                comm_recv_cnt += comm.Recv(comm_recv_buf, comm_recv_cnt, comm_recv_buf.Length - comm_recv_cnt);
                if (comm_recv_cnt < 4) continue;
                while (true) // 处理连续多帧
                {
                    if (comm_recv_buf[0] != 0xaa) comm_recv_cnt = 0;  // frame abnormal，clear data
                    if (comm_recv_cnt < CommPkg.head_size) break;                // frame not completed, continue recv
                    CommPkg pkg = new(ref comm_recv_buf);
                    if (pkg.len > comm_recv_cnt) break;          // frame not completed, continue recv
                    if (0 != CommPkg.GetChecksum(comm_recv_buf, pkg.len)) { comm_recv_cnt = 0; break; } // frame abnormal，clear data
                    string timestamp = DateTime.Now.ToString("[HH:mm:ss:fff] ");
                    // special handle m0 log cmd
                    if ((CommDiagCmd)pkg.cmd == CommDiagCmd.LOG)
                    {
                        string str = timestamp + Encoding.UTF8.GetString(comm_recv_buf, CommPkg.head_size, pkg.len - CommPkg.head_size);
                        if (m0_console != null) m0_console.StandardInput.Write(str);
                        m0_log_buf.Append(str);
                        m0_log = m0_log_buf.ToString();
                    }
                    // special handle cat1 log cmd
                    if ((CommDiagCmd)pkg.cmd == CommDiagCmd.LOG_CAT1)
                    {
                        string str = timestamp + Encoding.UTF8.GetString(comm_recv_buf, CommPkg.head_size, pkg.len - CommPkg.head_size);
                        if (cat1_console != null) cat1_console.StandardInput.Write(str);
                        cat1_log_buf.Append(str);
                        cat1_log = cat1_log_buf.ToString();
                    }
                    // check if frame is not related to current cmd, discard it
                    if ((CommDiagCmd)pkg.cmd == comm_send_cmd)
                    {
                        Array.Copy(comm_recv_buf, CommPkg.head_size, comm_frame_data, 0, pkg.len - CommPkg.head_size);
                        comm_send_cmd = CommDiagCmd.NONE;
                    }
                    // copy frame data
                    comm_recv_cnt -= pkg.len;    // goto next frame
                    if (comm_recv_cnt > 0)
                        Array.Copy(comm_recv_buf, pkg.len, comm_recv_buf, 0, comm_recv_cnt);
                }
            }
            comm_recv_task_cancel = false;   // clear flag
            return;
        }
        private void CommSend(CommDiagCmd cmd, byte[] buf, int len)
        {
            byte[] tx_buf = new byte[CommPkg.head_size + len];
            CommPkg pkg = new(ref tx_buf);
            pkg.flag = 0xaa;
            pkg.cmd = (byte)cmd;
            pkg.len = (ushort)tx_buf.Length;
            pkg.checksum = 0;

            for (int i = 0; i < len; i++) tx_buf[CommPkg.head_size + i] = (byte)buf[i];
            pkg.checksum -= CommPkg.GetChecksum(tx_buf, (uint)tx_buf.Length);
            comm_send_cmd = cmd;
            if (comm != null)
                comm.Send(tx_buf, tx_buf.Length);
        }
        private bool CommWaitResponse(CommDiagCmd cmd)
        {
            for (int i = 0; comm_send_cmd != CommDiagCmd.NONE; i++, System.Threading.Thread.Sleep(15)) // wait for response
            {
                if (i >= 20) // timeout, 200ms
                {
                    comm_send_cmd = CommDiagCmd.NONE;
                    return false;
                }
            }
            return true;
        }



        public ZqmIOTable gpio = new();
        public ulong gpio_val = 0;          // bitmap, gpio level high or low, 0-31:GPIOA, 32-63:GPIOB
        public DispItemCodecTb[] disp_item_codec_tb = new DispItemCodecTb[(int)DispItemIdx.ALL_NUM];
        // query device type - query device config package must less than 16 bytes
        private void QueryCfgDev()
        {
            dev_type = 0;
            byte[] buf = new byte[16];
            //send request;
            comm_send_cmd = CommDiagCmd.CFG_DEV;
            buf[0] = 0; // 配置控制台是否接收log信息
            if (cfg.recv_m0_log) buf[0] |= 1;
            if (cfg.recv_cat1_log) buf[0] |= 2;
            CommSend(comm_send_cmd, buf, 1);
            // wait for response or overtime
            if (!CommWaitResponse(comm_send_cmd)) return;
            // unpack device type, 32bits 
            int offset = 0;
            dev_type = BitConverter.ToUInt32(comm_frame_data, offset);
            offset += 4;
            // unpack gpio definition table
            gpio = KgStreamConvert.BytesToStuct<ZqmIOTable>(comm_frame_data[offset..]);
            offset += Marshal.SizeOf<ZqmIOTable>();
            // unpack gpio value
            gpio_val = BitConverter.ToUInt64(comm_frame_data, offset);
            offset += 8;
            // unpack display item codec table
            for (int i = 0; i < (int)DispItemIdx.ALL_NUM; i++)
            {
                disp_item_codec_tb[i].data = comm_frame_data[offset++];
            }
            disp_memory = new byte[comm_frame_data[offset++]];
            digital_num = comm_frame_data[offset++];
            // unpack entity key num and state
            entity_key_num = comm_frame_data[offset++];
            key_state = new uint[entity_key_num];
            // unpack key 
            byte key_map = comm_frame_data[offset++];
            for (int i = 0; i < 8; i++)
            {
                if ((key_map & (byte)(1 << i)) != 0)
                {
                    key_state[i] = BinaryPrimitives.ReadUInt32LittleEndian(comm_frame_data.AsSpan(offset, 4)); ;
                    offset += 4;
                }
            }
            // unpack virtual key num and key table
            virtual_key_num = comm_frame_data[offset++];
            key_tb = new byte[entity_key_num];      // 模拟器只关心实体键，和实体键最先对应虚拟键值(这个是用来画键图标和名称)
            for (int i = 0; i < entity_key_num; i++)
            {
                key_tb[i] = DevPreDef.KEY_INVALID;
                for (int j = 0; j < virtual_key_num; j++)  // 搜索和实体键最先对应虚拟键值
                {
                    if (comm_frame_data[offset + j] == i)
                    {
                        //key_tb[i] = comm_frame_data[offset + j];
                        key_tb[i] = (byte)j;
                        break;
                    }
                }
            }
            offset += virtual_key_num;
            return;
        }

        // query device type, only_value = true, only query value
        public void QueryCfgPm()
        {
            byte[] buf = new byte[16];
            //send request;
            comm_send_cmd = CommDiagCmd.CFG_PM;
            CommSend(comm_send_cmd, buf, 0);
            // wait for response or overtime
            if (!CommWaitResponse(comm_send_cmd)) return; // wait for response

            // deal with response
            int offset = 0;
            byte cnt = comm_frame_data[offset++];
            pm_tb = new ZqmParamsTable[cnt];
            pm_buf = new byte[cnt * 2];
            int pm_tb_size = Marshal.SizeOf<ZqmParamsTable>();
            for (int i = 0; i < cnt; i++)
            {
                pm_tb[i] = KgStreamConvert.BytesToStuct<ZqmParamsTable>(comm_frame_data[offset..(offset + pm_tb_size)]);
                pm_buf[2 * i] = (byte)(pm_tb[i].val & 0xff);
                pm_buf[2 * i + 1] = (byte)((pm_tb[i].val >> 8) & 0xff);
                offset += pm_tb_size;
            }
            return;
        }
        public void QueryCfgSt()
        {
            byte[] buf = new byte[16];
            //send request;
            comm_send_cmd = CommDiagCmd.CFG_ST;
            CommSend(comm_send_cmd, buf, 0);
            // wait for response or overtime
            if (!CommWaitResponse(comm_send_cmd)) return; // wait for response
            // deal with response
            int offset = 0;
            // unpack st
            int cnt = comm_frame_data[offset++];
            st_buf = new byte[Marshal.SizeOf<ZyStates>()];
            for (int i = 0; i < cnt; i++, offset += 4)
            {
                byte idx = comm_frame_data[offset++];
                Array.Copy(comm_frame_data, offset, st_buf, idx * 4, 4);
            }
        }


        private List<(byte, short)> pm_get_list = new(); // (pm索引, 参数值)
        private List<(byte, uint)> st_get_list = new(); // (st索引, 参数值)


        private byte[] led_icon_buf = new byte[16];
        // ZyStates buffer
        // exchange dbg data with device
        // 接收: 包头DbgPkgbuf[4] + 参数数量i[1] + 参数表(pm_idx[1] + pm_value[2])[i * 3] + 状态数量j[1] + 状态表(st_idx[1] + st_value[4])[j * 5] 
        //	+ GPIO变化(bit0-3表示以下有效, gpioa mode, gpiob mode, gpioa, gpiob)k[1] + GPIO_MODE[5] + GPIO[5]
        // 发送: 包头DbgPkgbuf[5] + 查询命令(0不需查询，1全量查询，2增量查询)q[1] + 参数数量i[1] + 参数表(pm_idx[1] + pm_value[2])[i * 3] + 状态数量j[1] + 状态表(st_idx[1] + st_value[4])[j * 5] 
        //	+ GPIO变化(bit0-5, 表示以下有效, gpioa mode, gpiob mode, gpioa, gpiob, adc, key)k[1] + GPIO_MODE[5] + GPIO[5] + ADC[4] + 键key[1]
        public void QuerySnapshot()
        {
            byte[] buf = new byte[1024];
            int idx = 0, num = 0;
            buf[idx++] = resp_cfg.data;

            //  ---------------------- query
            // 打包参数修改
            buf[idx++] = (byte)pm_set.Count;
            if (pm_set.Count > 0)
            {
                foreach (var pm in pm_set)
                {
                    buf[idx++] = pm.Item1;
                    BinaryPrimitives.WriteInt16LittleEndian(buf.AsSpan(idx, 2), pm.Item2);
                    idx += 2;
                }
                pm_set.Clear();
            }
            // 打包状态修改
            buf[idx++] = (byte)st_set.Count;
            if (st_set.Count > 0)
            {
                foreach (var st in st_set)
                {
                    buf[idx++] = st.Item1;
                    BinaryPrimitives.WriteUInt32LittleEndian(buf.AsSpan(idx, 4), st.Item2);
                    idx += 4;
                }
                st_set.Clear();
            }
            // 打包Key修改
            buf[idx++] = key_val;
            // 打包ADC修改
            num = idx++;
            buf[num] = 0;
            for (int i = 0; i < 4; i++)
            {
                if (last_adc[i] != adc[i])
                {
                    buf[num] |= (byte)(1 << i);
                    BinaryPrimitives.WriteUInt16LittleEndian(buf.AsSpan(idx, 2), adc[i]);
                    last_adc[i] = adc[i];
                    idx += 2;
                }
            }
            // 打包GPIO修改
            BinaryPrimitives.WriteUInt64LittleEndian(buf.AsSpan(idx, 8), gpio_val);
            idx += 8;
            //send request;
            comm_send_cmd = CommDiagCmd.SNAP;
            CommSend(comm_send_cmd, buf, idx);

            // wait for response or overtime
            if (!CommWaitResponse(comm_send_cmd)) return; // wait for response

            // ---------------------- response
            int offset = 0;
            // unpack pms
            int cnt = comm_frame_data[offset++];
            for (int i = 0; i < cnt; i++, offset += 2)
            {
                idx = comm_frame_data[offset++];
                if(pm_tb.Length > 0)
                {
                    pm_tb[idx].val = BinaryPrimitives.ReadInt16LittleEndian(buf[offset..(offset + 2)]);
                    Array.Copy(comm_frame_data, offset, pm_buf, idx * 2, 2);
                }
            }
            if (pm_tb.Length > 0)
                resp_cfg.pm = (CommPkg.GetChecksum8(pm_buf, (uint)pm_buf.Length) == comm_frame_data[offset]) ? (byte)0 : (byte)1;
            else 
                resp_cfg.pm = (byte)0;
            offset++;
            // unpack st
            cnt = comm_frame_data[offset++];
            for (int i = 0; i < cnt; i++, offset += 4)
            {
                idx = comm_frame_data[offset++];
                if(st_buf != null)
                    Array.Copy(comm_frame_data, offset, st_buf, idx * 4, 4);
            }
            // 还原st table
            if (st_buf != null)
            {
                st_tb = KgStreamConvert.BytesToStuct<ZyStates>(st_buf);
                resp_cfg.st = (CommPkg.GetChecksum8(st_buf, (uint)st_buf.Length) == comm_frame_data[offset]) ? (byte)0 : (byte)1;
            }
            else
                resp_cfg.pm = (byte)0;
            offset++;
            // unpack gpio value
            gpio_val = BinaryPrimitives.ReadUInt64LittleEndian(comm_frame_data.AsSpan(offset, 8));
            offset += 8;
            // unpack key 
            byte key_map = comm_frame_data[offset++];
            for (int i = 0; i < 8; i++)
            {
                if((key_map & (byte)(1 << i)) != 0)
                {
                    key_state[i] = BinaryPrimitives.ReadUInt32LittleEndian(comm_frame_data.AsSpan(offset, 4)); ;
                    offset += 4;
                }
            }
            // unpack disp state 
            if(resp_cfg.disp == 1) // unpack disp state tb
            {
                // TODOOO
            }
            // unpack disp memory
            Array.Copy(comm_frame_data, offset, disp_memory, 0, disp_memory.Length);
            offset += disp_memory.Length;
            pwm[0] = comm_frame_data[offset++];
            pwm[1] = comm_frame_data[offset];
            return;
        }
    }
}

// 